<?php
/**
 * Local Disk Object.
 * @package safsi_localdisk
 */

require_once('safsi_IObject.php');
require_once('safsi_localdisk_FileSystem.php');
require_once('safsi_localdisk_Container.php');
require_once('safsi_Util.php');

/**
 * Local Disk Object.
 * @package safsi_localdisk
 */
class safsi_localdisk_Object implements safsi_IObject {

    /**
     * File System.
     * @var safsi_localdisk_FileSystem
     */
    private $fileSystem;

    /**
     * Container.
     * @var safsi_localdisk_Container
     */
    private $container;

    /**
     * Name for this object.
     * @var string
     */
    private $name;

    /**
     * The content type for this object.
     * @var string
     */
    private $contentType;

    /**
     * The checksum for this object.
     * @var string
     */
    private $checksum;

    /**
     * The data for this object.
     * @var string
     */
    private $data;

    /**
     * The path leading to the object's file.
     * @var string
     */
    private $path;

    /**
     * Full path to the object's file.
     * @var string
     */
    private $pathToObject;

    /**
     * The file name portion of the object's name.
     * @var string
     */
    private $objectFileName;

    /**
     * Create a new container.
     * @param safsi_localdisk_FileSystem $fileSystem File System.
     * @param safsi_localdisk_Container $container Container.
     * @param string $name Name for this container.
     */
    public function __construct(safsi_localdisk_FileSystem $fileSystem, safsi_localdisk_Container $container, $name) {
        $this->fileSystem = $fileSystem;
        $this->container = $container;
        $this->name = $name;
    }

    /**
     * Get the object's name.
     * @return string Name
     */
    public function name() {
        return $this->name;
    }

    /**
     * Write object data to a resource stream.
     * @param resource $resource Content will be written to this stream
     */
    public function writeDataToStream($resource) {
        throw new safsi_exception_NotImplementedException(
            'write stream'
        );
    }


    /**
     * Read object data from a resource stream.
     * @param resource $resource Content will be read from this stream
     * @param string $contentType Content type if known
     * @param string $checksum Checksum if known
     */
    public function readDataFromStream($resource, $contentType = null, $checksum = null) {

        try {

            // Reset the content type.
            $this->contentType = $contentType;

            // Reset the checksum.
            $this->checksum = $checksum;

            // Put the contents of our resource in the requested file.
            file_put_contents($this->getPathToObject(), $resource);

        } catch (Exception $e) {
            throw new safsi_exception_IoException(
                'Could not write data from specified stream to "' . $this->getPathToObject() . '"',
                $e
            );
        }

    }


    /**
     * Write data to a file specified by name.
     * @param string $filename File name to write data
     */
    public function writeDataToFilename($filename) {

        try {

            // Get a resource handle for our object's file.
            $resource = fopen($this->getPathToObject(), 'r');

            // Put the contents of our resource in the requested file.
            file_put_contents($filename, $resource);

            // Close our resource.
            fclose($resource);

        } catch (Exception $e) {
            throw new safsi_exception_IoException(
                'Could not write data to specified filename "' . $filename .'"',
                $e
            );
        }

    }


    /**
     * Read data from a file specified by name.
     *
     * This implementation does a simple copy() operation.
     * @param string $filename File name to read data
     * @param string $contentType Content type if known
     * @param string $checksum Checksum if known
     */
    public function readDataFromFilename($filename, $contentType = null, $checksum = null) {

        try {

            // Reset the content type.
            $this->contentType = $contentType;

            // Reset the checksum.
            $this->checksum = $checksum;

            // TODO: Hopefully this is safe?
            copy($filename, $this->getPathToObject());

        } catch (Exception $e) {
            throw new safsi_exception_IoException(
                'Could not read data from specified filename "' . $filename .'"',
                $e
            );
        }

    }

    /**
     * Get the content of the object.
     * @return string Data
     */
    public function data() {
        return file_get_contents($this->getPathToObject());
    }

    /**
     * Set the content of the object.
     * @param string $data Data
     * @param string $contentType Content type if known
     * @param string $checksum Checksum if known
     */
    public function setData($data, $contentType = null, $checksum = null) {

        // Reset the content type.
        $this->contentType = $contentType;

        // Reset the checksum.
        $this->checksum = $checksum;

        $this->assertObjectExists();

        // Get the path to the object.
        $pathToObject = $this->getPathToObject();

        try {
            // Try to put the data into the object's file.
            file_put_contents($pathToObject, $data);
        } catch (Exception $e) {
            throw new safsi_exception_IoException('Could not write data to file "' . $pathToObject . '"', $e);
        }

        return true;

    }

    /**
     * Get the content type for the object.
     * @return string content type
     */
    public function contentType() {
        if ( $this->contentType === null ) {
            $this->contentType = safsi_Util::DETERMINE_MIME_TYPE($this->getPathToObject());
        }
        return $this->contentType;
    }


    /**
     * Get the checksum for the object.
     * @return string checksum
     */
    public function checksum() {
        if ( $this->checksum === null ) {
            $this->checksum = md5_file($this->getPathToObject());
        }
        return $this->checksum;
    }

    /**
     * Get the path leading to the object.
     *
     * For example, if the entire path is '/path/to/object', this
     * method would return '/path/to'.
     *
     * @return string Path leading to object
     */
    protected function getPath() {
        return $this->fileSystem->constructPath($this->container, $this);
    }

    /**
     * Get the path to the object.
     * @return string Path to object
     */
    protected function getPathToObject() {
        return $this->fileSystem->constructPathToObject($this->container, $this);
    }

    /**
     * Returns the "file name" portion of the object's name
     *
     * For example, if the entire path is '/path/to/object', this
     * method would return 'object'.
     *
     * @return string Object's file name
     */
    protected function getObjectFileName() {
        return $this->fileSystem->constructObjectFileName($this);
    }

    public function constructPath() {
        if ( $this->path === null ) {
            $this->path = $this->fileSystem->constructPath($this->container, $this);
        }
        return $this->path;
    }

    public function constructPathToObject() {
        if ( $this->pathToObject === null ) {
            $this->pathToObject = $this->fileSystem->constructPathToObject($this->container, $this);
        }
        return $this->pathToObject;
    }

    public function constructObjectFileName() {
        if ( $this->objectFileName === null ) {
            $this->objectFileName = $this->fileSystem->constructObjectFileName($this);
        }
        return $this->objectFileName;
    }

    public function mkDir($parts) {
        return $this->fileSystem->mkDir($parts);
    }

    public function assertObjectExists() {

        $this->mkDir(explode(DIRECTORY_SEPARATOR, $this->constructPath()));

        $pathToObject = $this->constructPathToObject();

        try {

            // Try to touch this file.
            touch($pathToObject);

        } catch (Exception $e) {
            print " [ aaa ]\n";
            print $e->toString();
            print " [ aaa ]\n";
            throw new safsi_exception_IoException('Could not write to file at "' . $pathToObject . '"', $e);

        }

        return true;

    }

}

?>
